﻿using System;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using nRoute.Components;
using nRoute.Components.Composition;
using nRoute.Components.Routing;
using nRoute.Navigation;
using nRoute.Utilities;

namespace nRoute.Mapping
{
    public class NavigationContentHandler
		 : IRouteHandler
    {

#region Constants and Variables

        private const string NO_PAGE_RESOLVED = "ResponseResolver Func did not return a non-null value for '{0}' navigation request.";
        private const string REQUEST_TYPE_REQUIRED = "NavigationRouteHandler can only respond to type NavigationRequest requests.";

        private readonly ITypeBuilder _typeBuilder;
        private readonly Func<ITypeBuilder, ParametersCollection, Object> _responseResolver;

#endregion

        public NavigationContentHandler(ITypeBuilder typeBuilder, Func<ITypeBuilder, ParametersCollection, Object> responseResolver)
        {
            Guard.ArgumentNotNull(typeBuilder, "typeBuilder");
            Guard.ArgumentNotNull(responseResolver, "responseResolver");

            _typeBuilder = typeBuilder;
            _responseResolver = responseResolver;
        }

#region Main Methods

        public IObservable<IUrlResponse> GetResponse(IRoutingContext context)
        {
            Guard.ArgumentNotNull(context, "context");
            Guard.ArgumentIsType(context.Request, typeof(NavigationRequest), "context.Request");

            // we initialize a lazy observer that is initialized when subscribed too
            var _relayObservable = Observable.Create<IUrlResponse>((o) =>
            {
                var _response = default(NavigationResponse);

                try
                {
                    // we get the response content, if it is empty we return
                    var _content = _responseResolver(_typeBuilder, context.ParsedParameters);
                    if (_content == null)
                    {
                        _response = new NavigationResponse((NavigationRequest)context.Request, ResponseStatus.UrlNotFound, _content, context.ParsedParameters);
                    }
                    else
                    {
                        // we get the navigation response
                        _response = new NavigationResponse((NavigationRequest)context.Request, ResponseStatus.Success, _content, context.ParsedParameters);
                    }
                }
                catch (Exception ex)
                {
                    _response = new NavigationResponse((NavigationRequest)context.Request, ResponseStatus.Exception, null, context.ParsedParameters, ex);
                }

                // and return
                o.OnNext(_response);
                o.OnCompleted();

                //                // USE TO TEST DELAY
                //                ThreadPool.QueueUserWorkItem(new WaitCallback((x) =>
                //                {
                //                    Thread.Sleep(2000);
                //#if SILVERLIGHT
                //                    var _dispatch = Deployment.Current.Dispatcher;
                //#else
                //                                        var _dispatch = Application.Current.Dispatcher;
                //#endif
                //                    _dispatch.BeginInvoke(new Action(() =>
                //                                              {
                //                                                  o.OnNext(_response);
                //                                                  o.OnCompleted();
                //                                              }));
                //                }));
                
                return Disposable.Empty;
            });
            
            // and return
            return _relayObservable;
        }

#endregion
        
#region IRouteHandler Members

        IObservable<IUrlResponse> IRouteHandler.GetResponse(IRoutingContext context)
        {
            return GetResponse(context);
        }

#endregion

    }
}